Skip to content

fix(ai-providers): read the system prompt and tools from the Pi 0.86+ transcript - #620

Open
JS-banana wants to merge 2 commits into
openpi-dev:mainfrom
JS-banana:fix/antigravity-transcript-context
Open

JS-banana wants to merge 2 commits into
openpi-dev:mainfrom
JS-banana:fix/antigravity-transcript-context

Conversation

@JS-banana

@JS-banana JS-banana commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Closes #619.

Pi 0.86.0 changed a custom provider's stream input from Context to a normalized TranscriptContext: the system prompt and tool declarations are folded into a leading transcript system message, and later system messages carry prompt/tool deltas. The Antigravity adapter still read context.systemPrompt / context.tools, so that leading system message fell through to the tool-result branch and became a functionResponse with no name:

GenerateContentRequest.contents[0].parts[0].function_response.name: Name cannot be empty.

Every request on Pi 0.86+ failed, and independently of the 400 the system prompt and all tool declarations were dropped. Cursor read the same two fields and silently fell back to its generic chat-only prompt with no tools.

Value

google-antigravity works again on Pi 0.86+ while the declared Pi 0.85.1 baseline keeps working unchanged, and the failure class is closed at the shared cause rather than per provider.

Approach

  • New extensions/ai-providers/transcript.ts: replays transcript system messages into the prompt and tool set currently in effect, mirroring Pi's getCurrentSystemPrompt / getCurrentTools without importing helpers that do not exist before Pi 0.86. applyTranscript returns the old Context untouched, so Pi <= 0.85.1 behavior is preserved exactly.
  • Antigravity buildRequestBody resolves the transcript once; convertMessages receives conversation messages only; buildTools takes the resolved tool list.
  • Google converter: only assistant turns are rewritten, and non-conversation roles or nameless tool results no longer become a functionResponse.
  • Cursor: buildCursorRequest / streamCursor resolve the transcript at the entry point.
  • No dependency, lockfile, or peer-range change — this lands independently of the baseline discussion in chore(deps): define and enforce the supported Pi baseline #328.

Validation

  • Differential against Pi 0.87.1's own replay (getCurrentSystemPrompt / getCurrentTools) across five message sets — leading system message; later section patch with toolsRemoved / toolsAdded; content plus sections; two system messages; no system message. Prompt text and tool set are identical in all five.
  • tests/extensions/ai-providers/antigravity.test.ts 58/58 and cursor.test.ts 36/36 (94 total on the current head). The four new regression tests fail when the provider changes are stashed, so they pin the regression rather than the implementation.
  • Cursor local HTTP/2 handshake regression covers both legacy Context and Pi 0.86+ transcript input. Assertions inspect the actual requestContextResult: patched/deleted prompt sections, the final tool names/descriptions/schemas after additions/removals, and the openpi provider identifier.
  • Previously completed real-account validation on Pi 0.87.1 (not rerun for the test-only follow-up), extension loaded with -ne -ns -np -nc --no-session -e extensions/ai-providers/index.ts:
  • Latest bun run test: Node tests 1889 passed / 0 failed / 5 skipped; Vitest 275 tests passed, but tests/web/app-render.spec.ts could not load because @xterm/addon-fit is missing. The overall command is not green.
  • bun run check: config/docs/discipline checks pass; check:web stops on missing @xterm/addon-fit / @xterm/xterm and related type errors. Changed-file Biome format/lint and git diff --check pass. tsc --noEmit still reports the same 20 environment errors involving node-pty / @xterm/*, with no errors in the changed test file.

Impact

… transcript

Pi 0.86.0 changed a custom provider's stream input from `Context` to a normalized
`TranscriptContext`: the system prompt and tool declarations are folded into a
leading transcript system message. The Antigravity adapter still read
`context.systemPrompt` / `context.tools`, so that system message fell through to
the tool-result branch and became a `functionResponse` with no name — Cloud Code
Assist rejects it as `contents[0].parts[0]`, failing every request. Even without
that 400, the system prompt and all tool declarations were silently dropped.

Add a shared transcript resolver that replays system messages into the prompt and
tool set currently in effect, mirroring Pi's `getCurrentSystemPrompt` /
`getCurrentTools` without importing helpers that do not exist before Pi 0.86, and
keep the `Context` shape working unchanged. The Google converter now skips
non-conversation roles and nameless tool results instead of emitting an invalid
`functionResponse`.

Cursor read the same two fields and silently fell back to its generic chat-only
prompt with no tools; it resolves the transcript as well.

Verified:
- differential: resolveTranscript matches Pi 0.87.1 getCurrentSystemPrompt /
  getCurrentTools across five message sets
- antigravity 58/58 and cursor 35/35; the new regression tests fail without the fix
- real google-antigravity turns on gemini-3.8-flash-high and claude-sonnet-4-6:
  plain reply plus a read-tool round trip
- node tests 1888 passed / 0 failed; typecheck has no new errors (the 20
  pre-existing ones come from node-pty/@XTerm not being installed here)
@JS-banana

Copy link
Copy Markdown
Contributor Author

CI note: Background terminals (Windows) failed on streamAntigravity bounds the full SSE lifetime after the first event.

That test is pre-existing and sets timeoutMs: 5, so the first SSE read has a 5 ms budget; on the Windows runner it lost that race and reported the first-event timeout (Timed out waiting for the first SSE event) instead of the lifetime timeout (next SSE event). It passed on Node 22.19.0 / 24 / 26 and in Web E2E in this same run, and this branch does not touch the SSE timing or failover path.

The Windows job is currently failing on other branches for unrelated tests — docs-only #618 failed it on not ok 215, and feat/session-child-admission failed it on not ok 804 — so this reads as the existing Windows flake rather than a regression from this change. I cannot re-run the job myself (no admin rights on this repository); a re-run should clear it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(antigravity): Pi 0.86+ 下 provider stream 收到 TranscriptContext,每次请求都以 400 失败

1 participant